package com.mini.game.dbbase;
import io.netty.util.HashedWheelTimer;
import io.netty.util.TimerTask;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Service
public class DataCenter {
    private final static Logger logger = LoggerFactory.getLogger(DataCenter.class);

    private Object lock = new Object();

    private HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(new DefaultThreadFactory("datacenter-timer"), 1, TimeUnit.SECONDS, 60);

    ExecutorService executorService = Executors.newFixedThreadPool(4, new DefaultThreadFactory("datacenter-db"));

    private Map<Class, TableData> cacheMap = new ConcurrentHashMap<>();

    public void timeOperate(TimerTask saveDataTask, int second) {
        hashedWheelTimer.newTimeout(saveDataTask, second, TimeUnit.SECONDS);
    }


    public <T extends IEntity> void cacheUpdate(T t, int delaySecond, JpaRepository jpaRepository) {
        TableData tableData = getEntityMap(t, delaySecond, jpaRepository);
        Serializable id = t.primaryKeyValue();
        WrapEntity oldWrapEntity = tableData.getDataMap().get(id);
        if (oldWrapEntity != null) {
            if (oldWrapEntity.getOperate().equals(DBOperate.DELETE)) {
                logger.error("cacheUpdate delete className={} id={} 之后再更新是有问题的操作", t.getClass().getSimpleName(), id);
            }
        }
        //如果存在就没必要再去创建了
        if (oldWrapEntity == null) {
            WrapEntity wrapEntity = new WrapEntity(id, t, null, DBOperate.UPDATE);
            tableData.getDataMap().put(id, wrapEntity);
            timeOperate((e) -> {
                executorService.execute(() -> {
                    tableData.getDataMap().remove(id);
                    save(jpaRepository, wrapEntity);
                });
            }, delaySecond);
        }
    }


    public <T extends IEntity> void cacheInsert(T t, int delaySecond, JpaRepository jpaRepository) {
        TableData tableData = getEntityMap(t, delaySecond, jpaRepository);
        Serializable id = t.primaryKeyValue();
        WrapEntity oldWrapEntity = tableData.getDataMap().get(id);
        //如果旧操作是删除 那么可以允许插入
        if (oldWrapEntity != null) {
            if (!oldWrapEntity.getOperate().equals(DBOperate.DELETE)) {
                logger.error("cacheInsert className={} 已存在 id={}", t.getClass().getSimpleName(), id);
                return;
            }
        }
        WrapEntity wrapEntity = new WrapEntity(id, t, null, DBOperate.INSERT);
        tableData.getDataMap().put(id, wrapEntity);
        timeOperate((e) -> {
            executorService.execute(() -> {
                tableData.getDataMap().remove(id);
                save(jpaRepository, wrapEntity);
            });
        }, delaySecond);
    }

    public <T extends IEntity> void cacheDelete(T t, int delaySecond, JpaRepository jpaRepository) {
        TableData tableData = getEntityMap(t, delaySecond, jpaRepository);
        //移除下旧数据 即使之前需要做插入 或者 更新 如果插入还没有执行
        Serializable id = t.primaryKeyValue();
        WrapEntity oldWrapEntity = tableData.getDataMap().remove(id);
//        if (oldWrapEntity == null) {
//            logger.error("cacheDelete className={} 不存存在 id={}", t.getClass().getSimpleName(), id);
//        }
        WrapEntity wrapEntity = new WrapEntity(id, t, null, DBOperate.DELETE);
        timeOperate((e) -> {
            executorService.execute(() -> {
                tableData.getDataMap().remove(id);
                //这里针对如果上一个操作是插入的操作做下安全处理
//                if (oldWrapEntity != null && oldWrapEntity.getOperate().equals(DBOperate.INSERT)) {
//                    save(jpaRepository, oldWrapEntity);
//                }
                deleteById(jpaRepository, wrapEntity);
            });
        }, delaySecond);
    }

    public void insert(JpaRepository jpaRepository, WrapEntity wrapEntity) {
        executorService.execute(() -> {
            try {
                save(jpaRepository, wrapEntity);
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
        });
    }

    public <T extends IEntity> void update(JpaRepository jpaRepository, WrapEntity wrapEntity) {
        executorService.execute(() -> {
            save(jpaRepository, wrapEntity);
        });
    }

    public void save(JpaRepository jpaRepository, WrapEntity wrapEntity) {
        try {
            IEntity entity = wrapEntity.getT();
            jpaRepository.save(entity);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    public void deleteById(JpaRepository jpaRepository, WrapEntity wrapEntity) {
        try {
            IEntity entity = wrapEntity.getT();
            jpaRepository.deleteById(entity.primaryKeyValue());
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    public <T extends IEntity> void delete(JpaRepository jpaRepository, WrapEntity wrapEntity) {
        executorService.execute(() -> {
            deleteById(jpaRepository, wrapEntity);
        });
    }

    public <T> TableData getEntityMap(T t, int delaySecond, JpaRepository jpaRepository) {
        Class cls = t.getClass();
        TableData tableData = cacheMap.get(cls);
        if (tableData == null) {
            synchronized (lock) {
                if (tableData == null) {
                    tableData = new TableData(delaySecond, jpaRepository);
                    cacheMap.put(cls, tableData);
                }
            }
        }
        return tableData;
    }

    @EventListener
    public void handle(ContextClosedEvent event){
        shutDown();
    }

    /**
     * 停服的时候调用下
     */
    public void shutDown() {
        logger.info("DataCenter shutDown");
        hashedWheelTimer.stop();
        for (Map.Entry<Class, TableData> entry : cacheMap.entrySet()) {
            TableData tableData = entry.getValue();
            if (tableData == null) {
                continue;
            }
            Map<Object, WrapEntity> dataMap = tableData.getDataMap();
            if (dataMap != null) {
                dataMap.forEach((key, value) -> {
                    DBOperate operate = value.getOperate();
                    JpaRepository jpaRepository = tableData.getJpaRepository();
                    if (operate.equals(DBOperate.INSERT)) {
//                        insert(jpaRepository, value);
                        save(jpaRepository, value);
                    } else if (operate.equals(DBOperate.UPDATE)) {
//                        update(jpaRepository, value);
                        save(jpaRepository, value);
                    } else if (operate.equals(DBOperate.DELETE)) {
//                        delete(jpaRepository, value);
                        deleteById(jpaRepository, value);
                    }
                });
            }
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(120, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            logger.error(e.getMessage(), e);
        }
        logger.info("DataCenter shutDown end");
    }
}
